#include "NewLife.h"
#include "bootutil.h"
#include "MemoryZone.h"

MemoryZone::MemoryZone(const char * name, u32 zone_start_pfn, u32 spanned_pages, u32 pages_min)
	: name(name), 
	  zone_start_pfn(zone_start_pfn), 
	  spanned_pages(spanned_pages), 
	  pages_min(pages_min),
	  free_pages(spanned_pages)
{
	kprintf("%s : start0x%x span0x%x\n", name, zone_start_pfn, spanned_pages);
	for(int i=0; i<MAX_ORDER; i++)
		this->free_area[i] = new struct free_area;
}

void MemoryZone::Initialize(Page *mem_map)
{
	this->zone_mem_map = mem_map + zone_start_pfn;
	int order = MAX_ORDER-1;
	Page * page = this->zone_mem_map;

	for(int page_remained=spanned_pages; page_remained>0; page_remained-=1<<order)
	{
		while(page_remained < (1<<order))
			order--;
/*
		kprintf("{{%s,%d,0x%x}}", name, order, (page-mem_map)<<12);
		this->free_area[order]->free_list.list_add_tail(page);
		this->free_area[order]->nr_free++;
		page += 1UL<<order;
*/
		// Check if this group of pages are contiguous.
		bool bContiguous = true;
		for(int i=0; i<1<<order; i++)
		{
			if(page[i].IsReserved())
			{
				bContiguous = false;
				break;
			}
		}

		if(bContiguous)
		{
			this->free_area[order]->free_list.list_add_tail(page);
			this->free_area[order]->nr_free++;
			page += 1UL<<order;
		}
		else
		{
			for(int i=0; i<1<<order; i++)
			{
				if(!page->IsReserved())
				{
					this->free_area[0]->free_list.list_add_tail(page);
					this->free_area[0]->nr_free++;
				}
				page++;
			}	
		}
	}
}

Page * MemoryZone::RemoveQueue(int order)
{
	struct free_area *area;
	unsigned int current_order;

	if(order>=MAX_ORDER)
		panic("Bad page frame request --> [%d]\n", order);

	for (current_order=order; current_order<MAX_ORDER; ++current_order) {
		area = this->free_area[current_order];
		if (!area->free_list.list_empty())
			break;
	}
	if(current_order >= MAX_ORDER)
		return NULL;

	Page *page = area->free_list.get_first_entry();
	page->list_del(page);
	page->ClearPagePrivate();
	area->nr_free--;
	this->free_pages -= 1UL << order;

	u32 size = 1 << current_order;
	while ((int)current_order > order) {
		current_order--;
		area = this->free_area[current_order];
		size >>= 1;
		Page * buddy = page + size;
		/* insert buddy as first element in the list */
		area->free_list.list_add(buddy);
		area->nr_free++;
		buddy->SetPagePrivate(current_order);
	}
	return page;
}

void MemoryZone::FreePagesBulk(Page *page, int order)
{
	Page * base = this->zone_mem_map;
	unsigned long buddy_idx, page_idx = page - base;
	Page * buddy, * coalesced;
	int order_size = 1 << order;

	this->free_pages += order_size;

	while (order < MAX_ORDER-1)
	{
		buddy_idx = page_idx ^ (1 << order);
		buddy = base + buddy_idx;
		if (!buddy->IsBuddy(order))
			break;
    
		buddy->list_del(buddy);
		this->free_area[order]->nr_free--;
		buddy->ClearPagePrivate();
		page_idx &= buddy_idx;
		order++;
	}

	buddy_idx = page_idx ^ (1 << order);
	buddy = base + buddy_idx;

	coalesced = base + page_idx;
	coalesced->SetPagePrivate(order);
	
	this->free_area[order]->free_list.list_add(coalesced);
	this->free_area[order]->nr_free++;
}

bool MemoryZone::IsContain(Page *page)
{
	Page * base = zone_mem_map;

	if(page < base)
		return false;

	u32 page_idx = page - base;
	if(page_idx >= spanned_pages)
		return false;
	else
		return true;
}

